/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package motorprediccion;

import motorprediccion.excepciones.datosInsuficientes;
import com.bbn.openmap.LatLonPoint;
import java.util.ArrayList;


/**
 *
 * @author dani
 */
public class Recorrido {

    //TODO: Esta clase tiene varias cosas que se superponen, como obtener una parada por el id o el orden que tienen dentro del recorrido
    //TODO: refactorizar esta clase
    //TODO: ver si es necesario agregar el atributo de orden dentro del recorrido para las paradas    

    private ArrayList<Parada> paradas;
    String nombre;
    int id, cantParadas;

    public int getId() {
        return id;
    }

    public Recorrido() {
        paradas = new ArrayList<Parada>(0);
        nombre = "";
        cantParadas = 0;
        id = 0;
    }

    public Recorrido(int ident, String nombre) {
        this.nombre = nombre;
        id = ident;
        paradas = new ArrayList<Parada>(0);
        cantParadas=0;
    }

    public void agregarParada(Parada p) {
        int j = 0;
        while (j< cantParadas && p.getOrden() > this.paradas.get(j).getOrden())
            j++;
        if (this.paradas.isEmpty() || j == cantParadas)
            paradas.add(p);
        else {
            this.paradas.add(j+1, p);
        }
        cantParadas++;
    }

    public Parada obtenerParada(int idParada) {
        int i = 0;
        while(i < cantParadas) {
            if(paradas.get(i).getId() == idParada)
                return paradas.get(i);
            else
                i++;
        }
        return null;
    }

    public Parada obtenerParadaOrden(int orden) {
        if (orden < this.paradas.size() && orden >= 0)
            return paradas.get(orden);
        else 
            return null;
    }
    /*
     * Devuelve la parada siguiente a la que tiene id igual a idParada. Si no existe una parada con idParada o idParada es la final, devuelve null
     */
    public Parada obtenerParadaSiguiente(int idParada) {
        int i = 0;
        while(i < cantParadas) {
            if(paradas.get(i).getId() == idParada) {
                if(i+1 < cantParadas)
                    return paradas.get(i+1);
                else
                    //return null;
                    return paradas.get(0); // si la parada anterior es la última retorno la primera parada del recorrido
            }
            else
                i++;
        }
        return null;
    }


    public int getCantidadParadas() {
        //return this.paradas.size();
        return cantParadas;
    }

    /*
     * Devuelve el rumbo de la parada p con respecto al norte geográfico en grados (0-359.9)
     */
    //TODO probar esto
    public float obtenerRumbo(Parada p) throws datosInsuficientes {
        if(this.existeParada(p)) {
            int n = this.obtenerOrden(p);
            LatLonPoint principio;
            LatLonPoint fin;

            if(n < this.getCantidadParadas()-1) { // si hay una parada mas adelante
                Parada siguiente = this.paradas.get(n+1);
                principio = p.getPosicion();
                fin = siguiente.getPosicion();
            }
            else { // si es la ultima parada, utilizo la parada anterior para calcular el rumbo
                if(n-1 >= 0){ // chequeo que haya una parada antes
                    Parada anterior = this.paradas.get(n-1);
                    principio = anterior.getPosicion();
                    fin = p.getPosicion();
                }
                else // se llega acá porque hay una sola parada en el recorrido
                    throw new datosInsuficientes("No hay suficientes paradas en el recorrido para calcular el rumbo");
            }

            float rumbo = (float) (principio.azimuth(fin) * 180 / Math.PI);

            if(rumbo < 0)
                rumbo = rumbo + 360;
            return rumbo;
        }
        else { // no existe la parada en el recorrido
            throw new datosInsuficientes("No hay suficientes paradas en el recorrido para calcular el rumbo");
        }
    }

    /*
     * Devuelve verdadero si existe la parada en el recorrido
     */
    boolean existeParada(Parada p) {
        int i = 0;
        while(i < cantParadas) {
            if(paradas.get(i).getId() == p.getId())
                return true;
            else
                i++;
        }
        return false;
    }

    /*
     * devuelve la posicion n-esima dentro del arreglo de paradas
     */
    public int obtenerOrden(Parada p) {
        /*int i = 0;
        while(i < paradas.size()) {
            if(paradas.get(i).getId() == p.getId())
                return i;
            else
                i++;
        }
        return -1;*/
        return p.getOrden();
    }

    /*
     * Devuelve la parada cuando se detecta un minimo en la distancia, buscando desde la parada que esta en la n-sima posicion.
     */
    //TODO: cambiar el parametro n para que sea una parada en lugar del lugar dentro del vector
    public Parada encontrarParadaCercana(LatLonPoint ubicacion, int n) {
        double distPrevia, distActual, distSiguiente;
        double tolerancia = 100.0; //es la minima distancia aceptable para aceptar un minimo, tener cuidado con esto
        
        if(n == 0) {// si se esta buscando n el principio
            if(cantParadas >= 2) {
                distActual = this.paradas.get(n).calcularDistancia(ubicacion);
                distSiguiente = this.paradas.get(n+1).calcularDistancia(ubicacion);

                if(distActual < distSiguiente) {
                    if(distActual < tolerancia) // ver si es un minimo aceptable
                        return this.paradas.get(n);
                    else //sino seguir buscando
                        return encontrarParadaCercana(ubicacion,n+1);
                }
                else
                    return encontrarParadaCercana(ubicacion,n+1);
            }
            else {   //existe una sola parada en el recorrido
                distActual = this.paradas.get(n).calcularDistancia(ubicacion);
                if(distActual < tolerancia)     // si el minimo es aceptable
                    return this.paradas.get(n);
                else
                    return null;
            }
        }
        else {        // si no se esta buscando desde el principio
            if(n < cantParadas - 1) {// si hay al menos un dato mas adelante para evaluar
                distPrevia = this.paradas.get(n-1).calcularDistancia(ubicacion);
                distActual = this.paradas.get(n).calcularDistancia(ubicacion);
                distSiguiente = this.paradas.get(n+1).calcularDistancia(ubicacion);

                if(distActual < distPrevia && distActual < distSiguiente) {   //si encuentra un minimo
                    if(distActual < tolerancia)     // si el minimo es aceptable
                        return this.paradas.get(n);
                    else
                        return this.encontrarParadaCercana(ubicacion, n + 1);
                }
                else
                    return this.encontrarParadaCercana(ubicacion, n +1);
            }
            else {        // si se llega al final del recorrido
                if(n<this.paradas.size()) {//controlo que n no sobrepase el limite del vector
                    distPrevia = this.paradas.get(n-1).calcularDistancia(ubicacion);
                    distActual = this.paradas.get(n).calcularDistancia(ubicacion);

                    if(distActual < distPrevia) // decreciente
                        if(distActual < tolerancia)     // si el minimo es aceptable
                            return this.paradas.get(n);
                        else
                            return null; // se llego al final y no se encontro un minimo aceptable
                    else        //creciente
                        return null; // no se encontro un minimo
                }
                else
                    return null;
                //TODO: devolver una excepcion porque está fuera del límite del vector

            }
        }
    }
    /**
     * Calcula la distancia entre paradas en el sentido del recorrido
     * @param origen: parada origen
     * @param destino: parada destino
     * @return distancia entre las paradas
     */  
    public float distancia(Parada origen, Parada destino) {
        int ordenOrigen, ordenDestino;
        float distancia = 0;

        ordenOrigen = this.obtenerOrden(origen);
        ordenDestino = this.obtenerOrden(destino);

        if(ordenOrigen <= ordenDestino) {
            int n = ordenOrigen;
            while(n < ordenDestino) { //Si distancia == 0, no itera nunca
                distancia += this.paradas.get(n-1).calcularDistancia(this.paradas.get(n).getPosicion());
                n++;
            }
        }
        else {
            distancia = distancia(origen,this.paradas.get(cantParadas-1));
            distancia += this.paradas.get(cantParadas-1).calcularDistancia(paradas.get(0).getPosicion());
            distancia += distancia(paradas.get(0),destino);
        }
       return distancia;
    }
}
